#ifndef _CANARY2_NET_IPADDR_H_
#define _CANARY2_NET_IPADDR_H_

#include <string_view>
#include <string>
#include <sys/socket.h>
#include <concepts>
#include <initializer_list>
#include <compare>
#include <array>
#include <span>

namespace wilson::net
{
    class ipv4_address
    {
    public:
        constexpr ipv4_address() = default;
        constexpr ipv4_address(const uint8_t a, const uint8_t b, 
                               const uint8_t c, const uint8_t d) noexcept
            : ia_data{ {a,b,c,d} }
        {
        }

        explicit constexpr ipv4_address(uint32_t a) noexcept
            : ia_data{ { static_cast<uint8_t>((a >> 24) & 0xFF), 
                         static_cast<uint8_t>((a >> 16) & 0xFF), 
                         static_cast<uint8_t>((a >>  8) & 0xFF), 
                         static_cast<uint8_t>((a      ) & 0xFF) } }
        {
        }

        ipv4_address(const ipv4_address& other) noexcept;

        ipv4_address& operator+=(int i);
        ipv4_address& operator-=(int i);
        ipv4_address& operator++();
        ipv4_address& operator--();
        ipv4_address  operator++(int);
        ipv4_address  operator--(int);

        ::std::string to_string() const;
        uint32_t      to_uint32() const noexcept;
        ::std::span<const uint8_t> as_uint8s() const noexcept;
        
    private:
        ::std::array<uint8_t, 4> ia_data{};
    };

    ::std::strong_ordering operator<=>(ipv4_address a, ipv4_address b);   

    class ipv6_address
    {
    public:
        constexpr ipv6_address() = default;

        ipv6_address(::std::initializer_list<uint16_t> vals);
        ipv6_address(::std::span<uint16_t> vals);
        ipv6_address(::std::span<uint32_t> vals);
        ipv6_address(::std::span<uint64_t> vals);

        ::std::string to_string() const;
        const uint8_t* data() const noexcept;
        ::std::span<const uint64_t> as_uint64s() const noexcept;
        ::std::span<const uint16_t> as_uint16s() const noexcept;

        // Unicast embedded ipv6 address translator
        friend ipv6_address to_v6addr(ipv4_address v4a);
        
    private:
        ::std::array<uint16_t, 8> i6a_data{};
    };

    ::std::strong_ordering 
    operator<=>(const ipv6_address& a, const ipv6_address& b);   
}

#endif
